package com.jzoffer;

/*
* 给定一个数组和滑动窗口的大小，找出所有滑动窗口里数值的最大值。
* 例如，如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3，
* 那么一共存在6个滑动窗口，他们的最大值分别为{4,4,6,6,6,5}；
* 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个：
*  {[2,3,4],2,6,2,5,1}， {2,[3,4,2],6,2,5,1}， {2,3,[4,2,6],2,5,1}， {2,3,4,[2,6,2],5,1}， {2,3,4,2,[6,2,5],1}， {2,3,4,2,6,[2,5,1]}。
*
* */

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;

public class TheMaxValueOfWindows {
    public static void main(String[] args) {
        int []arr = {2,3,4,2,6,2,5,1};
        int k = 3;
        ArrayList<Integer> integers = maxInWindows(arr, k);
        System.out.println(Arrays.toString(integers.toArray()));
    }

    /**
     * 题目：滑动窗口的最大值
     * 思路：滑动窗口应当是队列，但为了得到滑动窗口的最大值，队列序可以从两端删除元素，因此使用双端队列。
     *     原则：
     *     对新来的元素k，将其与双端队列中的元素相比较
     *     1）前面比k小的，直接移出队列（因为不再可能成为后面滑动窗口的最大值了!）,
     *     2）前面比k大的X，比较两者下标，判断X是否已不在窗口之内，不在了，直接移出队列
     *     队列的第一个元素是滑动窗口中的最大值
     */
    public static ArrayList<Integer> maxInWindows(int [] num, int size){
        ArrayList<Integer> windows = new ArrayList<>();
        if(num == null || num.length < 1 || size < 1 || num.length < size){
            return windows;
        }
        // 存储的是数组索引 不是具体元素
        LinkedList <Integer> queue = new LinkedList<>();
        for(int i = 0;i < size - 1;i++){
            while (!queue.isEmpty() && num[queue.getLast()] < num[i]){
                queue.removeLast();
            }
            queue.addLast(i);
        }
        for(int i = size - 1; i < num.length;i++){
            // 移除最后比外来小的元素，最后加入
            while (!queue.isEmpty() && num[queue.getLast()]  < num[i]){
                queue.removeLast();
            }
            queue.addLast(i);
            if(i - queue.getFirst() + 1 > size){
                queue.removeFirst();
            }
            windows.add(num[queue.getFirst()]);
        }
        return windows;
    }
}
